#ifndef _DEVICE_H
#define _DEVICE_H

#include <compiler.h>
#include <list.h>

#ifdef __TOP_HALF__

enum devices {
	DEVICE_UART		= 1,
	DEVICE_NAND		= 2,
};

struct device {
	unsigned int id;
	int index;
	void *priv;
};

#define DEVICE(_id, _index, _priv)										\
	struct device __device_##_id										\
		__always_unused __aligned(4) __section(.__devices) = {			\
			.id    = _id,												\
			.index = _index,											\
			.priv  = _priv,												\
	}

struct device *device_lookup_by_id(unsigned int id, int index);

#else

#define MINORBITS		20
#define MINORMASK		((1U << MINORBITS) - 1)

#define MAJOR(dev)		((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)		((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))

#define DEV_FLAGS_CAN_READ			(0x1)
#define DEV_FLAGS_CAN_WRITE			(0x2)
#define DEV_FLAGS_CAN_RDWR			(DEV_FLAGS_CAN_READ | DEV_FLAGS_CAN_WRITE)
#define DEV_FLAGS_CAN_SEEK			(0x4)
#define DEV_FLAGS_CAN_NONBLOCK		(0x8)		
#define DEV_FLAGS_CAN_SYNC			(0x10)		
#define DEV_FLAGS_NO_CLOSING_FLUSH	(0x20)
#define DEV_FLAGS_MASK				(0x3f)

#define dev_flags_is_can_read(flags)			((flags) & DEV_FLAGS_CAN_READ)	
#define dev_flags_is_can_write(flags)			((flags) & DEV_FLAGS_CAN_WRITE)
#define dev_flags_is_can_rdwr(flags)			(((flags) & DEV_FLAGS_CAN_RDWR) == DEV_FLAGS_CAN_RDWR)
#define dev_flags_is_can_seek(flags)			((flags) & DEV_FLAGS_CAN_SEEK)
#define dev_flags_is_can_nonblock(flags)		((flags) & DEV_FLAGS_CAN_NONBLOCK)
#define dev_flags_is_can_sync(flags)			((flags) & DEV_FLAGS_CAN_SYNC)
#define dev_flags_is_no_closing_flush(flags)	((flags) & DEV_FLAGS_NO_CLOSING_FLUSH)

#define dev_flags_set_can_read(flags)			((flags) |= DEV_FLAGS_CAN_READ)
#define dev_flags_set_can_write(flags)			((flags) |= DEV_FLAGS_CAN_WRITE)
#define dev_flags_set_can_rdwr(flags)			((flags) |= DEV_FLAGS_CAN_RDWR)
#define dev_flags_set_can_seek(flags)			((flags) |= DEV_FLAGS_CAN_SEEK)
#define dev_flags_set_can_nonblock(flags)		((flags) |= DEV_FLAGS_CAN_NONBLOCK)
#define dev_flags_set_can_sync(flags)			((flags) |= DEV_FLAGS_CAN_SYNC)
#define dev_flags_set_no_closing_flush(flags)	((flags) |= DEV_FLAGS_NO_CLOSING_FLUSH)

#define dev_flags_clr_can_read(flags)			((flags) &= ~DEV_FLAGS_CAN_READ)
#define dev_flags_clr_can_write(flags)			((flags) &= ~DEV_FLAGS_CAN_WRITE)
#define dev_flags_clr_can_rdwr(flags)			((flags) &= ~DEV_FLAGS_CAN_RDWR)
#define dev_flags_clr_can_seek(flags)			((flags) &= ~DEV_FLAGS_CAN_SEEK)
#define dev_flags_clr_can_nonblock(flags)		((flags) &= ~DEV_FLAGS_CAN_NONBLOCK)
#define dev_flags_clr_can_sync(flags)			((flags) &= ~DEV_FLAGS_CAN_SYNC)
#define dev_flags_clr_no_closing_flush(flags)	((flags) &= ~DEV_FLAGS_NO_CLOSING_FLUSH)

#define DEV_OFLAGS_READ			(0x1)
#define DEV_OFLAGS_WRITE		(0x2)
#define DEV_OFLAGS_RDWR         (DEV_OFLAGS_READ | DEV_OFLAGS_WRITE)
#define DEV_OFLAGS_SEEK			(0x4)
#define DEV_OFLAGS_NONBLOCK     (0x8)
#define DEV_OFLAGS_SYNC			(0x10)		
#define DEV_OFLAGS_MASK			(0x1f)

#define dev_oflags_is_read(oflags)			((oflags) & DEV_OFLAGS_READ)
#define dev_oflags_is_write(oflags)			((oflags) & DEV_OFLAGS_WRITE)
#define dev_oflags_is_rdwr(oflags)			(((oflags) & DEV_OFLAGS_RDWR) == DEV_OFLAGS_RDWR)
#define dev_oflags_is_seek(oflags)			((oflags) & DEV_OFLAGS_SEEK)
#define dev_oflags_is_nonblock(oflags)		((oflags) & DEV_OFLAGS_NONBLOCK)
#define dev_oflags_is_sync(oflags)			((oflags) & DEV_OFLAGS_SYNC)

#define dev_oflags_set_read(oflags)			((oflags) |= DEV_OFLAGS_READ)
#define dev_oflags_set_write(oflags)		((oflags) |= DEV_OFLAGS_WRITE)
#define dev_oflags_set_rdwr(oflags)			((oflags) |= DEV_OFLAGS_RDWR)
#define dev_oflags_set_seek(oflags)			((oflags) |= DEV_OFLAGS_SEEK)
#define dev_oflags_set_nonblock(oflags)		((oflags) |= DEV_OFLAGS_NONBLOCK)
#define dev_oflags_set_sync(oflags)			((oflags) |= DEV_OFLAGS_SYNC)

#define dev_oflags_clr_read(oflags)			((oflags) &= ~DEV_OFLAGS_READ)
#define dev_oflags_clr_write(oflags)		((oflags) &= ~DEV_OFLAGS_WRITE)
#define dev_oflags_clr_rdwr(oflags)			((oflags) &= ~DEV_OFLAGS_RDWR)
#define dev_oflags_clr_seek(oflags)			((oflags) &= ~DEV_OFLAGS_SEEK)
#define dev_oflags_clr_nonblock(oflags)		((oflags) &= ~DEV_OFLAGS_NONBLOCK)
#define dev_oflags_clr_sync(oflags)			((oflags) &= ~DEV_OFLAGS_SYNC)

#define DEV_SEEK_BEG  0   
#define DEV_SEEK_CUR  1   
#define DEV_SEEK_END  2 

struct device {
    char *name;
    dev_t devt;
    unsigned int oflags;	
	unsigned int flags;
	int refcount;
    void *priv;	
    struct driver *drv; 
	struct list_head n_dev_list;
	struct clk *clk;
};

struct device_operations {
    int (*open)(struct device *dev);
    void (*close)(struct device *dev);
    int (*read)(struct device *dev, void *buf, unsigned int len);
    int (*write)(struct device *dev, const void *buf, unsigned int len);
    loff_t (*seek)(struct device *dev, loff_t offset, unsigned int origin);
    int (*ioctl)(struct device *dev, unsigned int cmd, unsigned int arg);
    void (*flush)(struct device *dev);
	int (*match)(struct device *dev, struct driver *drv);
    int (*probe)(struct device *dev, struct driver *drv);
};

struct driver {
    char *name;
	dev_t min_devt, max_devt;
	struct list_head dev_list;
    struct device_operations *ops;
    void *priv;
};

#define device_to_driver(dev)	((dev)->drv)

#define DEVICE(_id, _name, _devt, _flags, _priv)                        \
    struct device _id													\
		__aligned(4) __section(.__devices) = {                          \
            .name  = _name,                                             \
			.devt  = _devt,                                             \
			.flags = _flags,                                            \
			.priv  = _priv,                                             \
	}

#define DRIVER(_id, _name, _min_devt, _max_devt, _ops, _priv)           \
    struct driver _id													\
        __aligned(4) __section(.__drivers) = {                          \
            .name      = _name,                                         \
            .min_devt  = _min_devt,                                     \
            .max_devt  = _max_devt,                                     \
			.ops	   = _ops,                                          \
			.priv      = _priv,                                         \
	}

struct device *device_lookup_by_name(char *name);
struct device *device_lookup(dev_t devt);
int device_open(struct device *dev, unsigned int oflags);
void device_close(struct device *dev);
void device_flush(struct device *dev);
int device_read(struct device *dev, void *buf, unsigned int len);
int device_write(struct device *dev, const void *buf, unsigned int len);
loff_t device_seek(struct device *dev, loff_t offset, unsigned int origin);
int device_ioctl(struct device *dev, unsigned int cmd, unsigned int arg);
	
int register_driver(struct driver *driver);

#endif 

extern struct device __devices_start[], __devices_end[];

#define for_each_device(p)                                              \
    for ((p) = __devices_start; (p) < __devices_end; (p)++)       

#ifndef __TOP_HALF__

extern struct driver __drivers_start[], __drivers_end[];

#define for_each_driver(p)                                              \
    for ((p) = __drivers_start; (p) < __drivers_end; (p)++)       

#endif 

#endif /* _DEVICE_H */

